#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include <limits.h>
#include <elf.h>
#include <sys/mman.h>
#include <dlfcn.h>

int main(int argc, char **argv)
{
  void *base = 0, *sometimes_base;
  int i,j = 0;

  int SOAP_PAGESIZE = 4096;
  void *MEMSIZE = (void *)ULONG_MAX;

  for( ; base <= (void *)((ptrdiff_t)MEMSIZE ^ SOAP_PAGESIZE) ; 
      base += SOAP_PAGESIZE) {

    access(base, F_OK);
    if(EFAULT != errno && memcmp(ELFMAG, base, 4) == 0) {
        printf("ELF!!!\n%p\n", base);
        if( ((unsigned char *)base)[EI_CLASS] == ELFCLASS32 ) {
          Elf32_Ehdr *eh = base;  /* Elf header */
          Elf32_Half p_count = 0; /* Number of program headers */
          int ELFPHENTSIZE = eh->e_phentsize;

          printf("32 bits\n");
          if( eh->e_type == ET_DYN )
            printf("Library\n");
          else
            continue;

          p_count = eh->e_phnum;
          
          /* 
           * Iterate over program headers to find the PT_DYNAMIC one.
           * This is the header where the .dynamic section is loaded,
           * it's the only section in that header.
           *
           * Looking for it here is easiear because program headers
           * are easy to look at in memory. The information about
           * section location in the ELF header is broken and refereces
           * to file offsets that don't correlate to actual memory
           * locations after mapping the library in memory.
           */
          for(i = 0; i < p_count; i++) {
            Elf32_Phdr *ph = base + eh->e_phoff + i * ELFPHENTSIZE;
            
            if(ph->p_type == PT_DYNAMIC) {
              Elf32_Dyn *dynit = NULL;
              
              printf("Dynamic program header!! %d\n", i);
              dynit = base + ph->p_vaddr;

              Elf32_Sym *symtab = NULL;
              char *strtab = NULL;
              Elf32_Word *hashtab = NULL;
              Elf32_Rel *plttab = NULL;

              /* Iterate over .dynamic */
              while(dynit->d_tag != DT_NULL) {
                /*
                 * d_ptr addresses are already resolved by the linker in
                 * ia32 Linux, but not in Android. Possibly a drift in 
                 * linker implementations.
                 *
                 */
#ifdef __i386__
                sometimes_base = 0;
#elif __arm__
                sometimes_base = base;
#endif

                if(dynit->d_tag == DT_SYMTAB)
                  symtab = sometimes_base + dynit->d_un.d_ptr;
                if(dynit->d_tag == DT_STRTAB)
                  strtab = sometimes_base + dynit->d_un.d_ptr;
                
                /* We need the hash table to know how many symbols 
                 * we have */
                if(dynit->d_tag == DT_HASH)
                  hashtab = sometimes_base + dynit->d_un.d_ptr;

                /* We sometimes need JMPSLOT relocations */
                if(dynit->d_tag == DT_JMPREL)
                  plttab = sometimes_base + dynit->d_un.d_ptr;

                dynit++;
              }

              if(symtab == NULL || strtab == NULL || hashtab == NULL) {
                printf("Can't find symbols in this library!!\n");
                continue;
              }

              printf("Symbol table: %p\n", symtab);
              printf("String table: %p\n", strtab);
              printf("Hash table: %p\n", hashtab);
              printf("PLT: %p\n", plttab);

              Elf32_Word symbol_count = hashtab[1];
              printf("Iterating over %d symbols\n", symbol_count);

              /*
               * Iterate over symbols, search for dlopen and
               * dlsym in the symbol table.
               *
               */
              Elf32_Sym *symit = symtab;
              void *(*found_dlopen)(const char *, int) = NULL;
              void *(*found_dlsym)(void *, const char *) = NULL;
              for(; symbol_count > 0; symit++, symbol_count--) {
                char *symbol = strtab + symit->st_name;
                if(strcmp("__libc_dlopen_mode", symbol) == 0 ||
                   strcmp("dlopen", symbol) == 0) {
                  found_dlopen = base + symit->st_value;
                  printf("dlopen: %p\n", found_dlopen);
                }
                if(strcmp("__libc_dlsym", symbol) == 0 ||
                   strcmp("dlsym", symbol) == 0) {
                  found_dlsym = base + symit->st_value;
                  printf("dlsym: %p\n", found_dlsym);
                }

              }

              /*
               * If symbols are undefined search for them on the PLT.
               * This is the case on Android/bionic.
               *
               */
              if(found_dlopen == base || found_dlsym == base) {
                printf("Searching for functions in the PLT\n");
                
                int done = 0;
                Elf32_Rel *pltit = plttab;
                while(done != 2) {
                  Elf32_Sym *symbol = ELF32_R_SYM(pltit->r_info) + symtab;
                  char *symbol_name = symbol->st_name + strtab;

                  if(strcmp("dlopen", symbol_name) == 0) {
                    found_dlopen = *(unsigned int *)(base + pltit->r_offset);
                    done++;
                    printf("dlopen: %p\n", found_dlopen);
                  }
                  if(strcmp("dlsym", symbol_name) == 0) {
                    found_dlsym = *(unsigned int *)(base + pltit->r_offset);
                    done++;
                    printf("dlsym: %p\n", found_dlsym);
                  }

                  pltit++;
                }

                printf("Finished searching\n");

              }

              /*
               * If everything worked we should be able to call
               * printf without goind through the PLT.
               *
               * */
              if(found_dlopen != NULL && found_dlsym != NULL) {
#ifdef __i386__
                /* This won't work on a real i386 system */
                void *lib = found_dlopen("/lib32/libc.so.6", 2);
#elif __arm__
                void *lib = found_dlopen("/system/lib/libc.so", 2);
#endif
                /* The type for printf is too complicated */
                void (*func)(char *) = found_dlsym(lib, "printf");
                printf("printf: %p\n", func);
                printf("printf: %p\n", printf);
                (*func)("--> String from direct call to printf <--\n");
              } else 
                printf("Not libc\n");

            }
          }
        }

        /* 
         *
         *  TODO: Try to code the 64 bit versaion without too much
         *  copy&paste  from the 32 bits one.
         *
         * */
    }
  }

  printf("\n");

  return 0;
}

